package com.arcaneshift.servlet.action.game;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.arcaneshift.manager.action.game.DeckManager;
import com.arcaneshift.model.action.game.BattleEngine;
import com.arcaneshift.model.action.game.BattlePlayer;
import com.arcaneshift.model.action.game.Deck;
import com.arcaneshift.model.action.game.Player;
import com.arcaneshift.model.action.user.GameSession;
import com.arcaneshift.model.request.RegisterQueuePlayerRequest;
import com.arcaneshift.model.response.RegisterQueuePlayerResponse;
import com.arcaneshift.model.validators.system.SessionValidator;
import com.arcaneshift.security.errors.PlayerError;
import com.arcaneshift.servlet.util.network.RequestAction;
import com.arcaneshift.servlet.util.parser.RegisterQueuePlayerXMLParser;
import com.arcaneshift.util.GameCacheContainer;
import com.arcaneshift.util.PlayerCacheContainer;
import com.arcaneshift.util.SessionCacheContainer;
import com.common.model.abstracts.AbstractRequest;
import com.common.model.abstracts.AbstractValidator;
import com.common.model.action.Session;
import com.common.model.validators.ActionValidator;
import com.common.security.abstracts.AbstractError;
import com.common.security.errors.ParserError;
import com.common.security.exceptions.ManagerException;
import com.common.security.exceptions.ParserException;
import com.common.servlet.abstracts.AbstractParserServlet;
import com.common.servlet.interfaces.IParser;
import com.google.appengine.api.channel.ChannelService;
import com.google.appengine.api.channel.ChannelServiceFactory;

public class RegisterQueuePlayerServlet extends AbstractParserServlet {
	private static final long serialVersionUID = 1L;
	private static Set<AbstractValidator> validators = new HashSet<AbstractValidator>();
	
	private RegisterQueuePlayerRequest requestContent;
	private RegisterQueuePlayerXMLParser parser = new RegisterQueuePlayerXMLParser();
	
	static {
		validators.add(new ActionValidator(RequestAction.REGISTER_QUEUE_PLAYER));
		validators.add(new SessionValidator());
	}
	
	@Override
	public void handle(HttpServletRequest request, HttpServletResponse response) {
		try {
			RegisterQueuePlayerResponse addQueuePlayerResponse = new RegisterQueuePlayerResponse();
			GameSession session = SessionCacheContainer.getSession(requestContent.getSessionId());
			Player player = PlayerCacheContainer.getPlayer(session.getUserId());
			AbstractError playerError;
			
			playerError = isRegistered(player);
			if (playerError != null) {
				error(playerError, response);
				return;
			}
			
			playerError = isPlaying(session);
			if (playerError != null) {
				error(playerError, response);
				return;
			}
			
			Player enemyPlayer = PlayerCacheContainer.getAvailablePlayer();
			Logger.getAnonymousLogger().severe("PLAYER: " + (enemyPlayer == null));
			player = new Player(session);
			
			if (enemyPlayer != null) {
				BattleEngine game = createGame(player, enemyPlayer);
				BattlePlayer battlePlayer = game.getPlayerById(player.getSession().getUserId());
				
				addQueuePlayerResponse.setRequestAction(requestContent.getAction());
				addQueuePlayerResponse.setGameId(game.getId());
				addQueuePlayerResponse.setPlayerNumber(battlePlayer.getPlayerNumber());
				addQueuePlayerResponse.setChannel(battlePlayer.getPlayer().getChannel());
			} else {
				PlayerCacheContainer.addPlayer(player);
				addQueuePlayerResponse.setRequestAction(requestContent.getAction());
			}
			
			response.setContentType("text/xml");
			response.getWriter().write(parser().parseResponse(addQueuePlayerResponse));
			response.getWriter().close();
		} catch (IOException ioException) {
			error(new ParserError(ParserError.STREAM_ERROR), response);
		} catch (ParserException parserException) {
			exception(parserException, response);
		} catch (ManagerException managerException) {
			exception(managerException, response);
		}
	}

	/**
	 * The method checks if the player exists, and if it does, it returns an error, 
	 * depending on his game id. If it has a game id, he is in-game, else he is in queue.
	 * 
	 * @param player
	 * @return PlayerError / null
	 * <p>
	 * PlayerError:
	 * <br> - Player Already in Game
	 * <br> - Player Already in Queue
	 * </p>
	 */
	private PlayerError isRegistered(Player player) {
		if (player != null) {
			if (player.getGameId() != null) {
				return new PlayerError(PlayerError.PLAYER_ALREADY_IN_GAME);
			} else {
				return new PlayerError(PlayerError.PLAYER_ALREADY_IN_QUEUE);
			}
		}
		return null;
	}
	
	/**
	 * The method checks if the user's id was found in the GameContainer. If it 
	 * 
	 * @param session
	 * @return PlayerError / null
	 * <p>
	 * PlayerError:
	 * <br> - Player Already in Game
	 * </p>
	 */
	private PlayerError isPlaying(Session session) {
		if (GameCacheContainer.containsId(session.getUserId())) {
			return new PlayerError(PlayerError.PLAYER_ALREADY_IN_GAME);
		}
		
		return null;
	}
	
	private String createChannel(Long userToken) {
		try {
			ChannelService channelService = ChannelServiceFactory.getChannelService();
			return channelService.createChannel(userToken.toString());
		} catch (Exception channelException) {
			return null;
		}
	}
	
	private BattleEngine createGame(Player player, Player enemyPlayer) throws ManagerException {
		BattleEngine game = new BattleEngine();
		DeckManager deckManager = new DeckManager();
		
		//enemyPlayer.setChannel(createChannel(player2.getSession().getUserId())); -- This is the AI, he doesn't need a channel.
		player.setChannel(createChannel(player.getSession().getUserId()));
		
		Deck playerDeck = deckManager.getById(player.getSession().getDeckId());
		Deck enemyDeck = deckManager.getById(enemyPlayer.getSession().getDeckId());
		
		playerDeck.shuffle();
		enemyDeck.shuffle();
		
		game.addPlayer(enemyPlayer, enemyDeck);
		game.addPlayer(player, playerDeck);
		
		GameCacheContainer.addGame(game);
		enemyPlayer.setGameId(game.getId());
		player.setGameId(game.getId());
		
		removePlayer(player);
		PlayerCacheContainer.updatePlayer(enemyPlayer);
		
		return game;
	}
	
	private void removePlayer(Player player) {
		PlayerCacheContainer.deletePlayer(player);
		SessionCacheContainer.deleteSession(player.getSession());
	}
	
	@Override
	public void setRequest(AbstractRequest abstractRequest) {
		requestContent = (RegisterQueuePlayerRequest) abstractRequest;
	}

	@Override
	public Set<AbstractValidator> getValidators() {
		return validators;
	}

	@Override
	public IParser parser() {
		return parser;
	}
}
